#include "tcpmgr.h"
#include "usermgr.h"
#include <QAbstractSocket>
#include <QJsonDocument>

TcpMgr::TcpMgr():_host(""),_port(0),_b_recv_pending(false),_message_id(0),_message_len(0)
{
    QObject::connect(&_socket, &QTcpSocket::connected, [&]() {
           qDebug() << "Connected to server!";
           // 连接建立后发送消息
            emit sig_con_success(true);
       });

       QObject::connect(&_socket, &QTcpSocket::readyRead, [&]() {
           // 当有数据可读时，读取所有数据
           // 读取所有数据并追加到缓冲区
           _buffer.append(_socket.readAll());

           QDataStream stream(&_buffer, QIODevice::ReadOnly);
           stream.setVersion(QDataStream::Qt_5_0);

           forever {
                //先解析头部
               if(!_b_recv_pending){
                   // 检查缓冲区中的数据是否足够解析出一个消息头（消息ID + 消息长度）
                   if (_buffer.size() < static_cast<int>(sizeof(quint16) * 2)) {
                       return; // 数据不够，等待更多数据
                   }

                   // 预读取消息ID和消息长度，但不从缓冲区中移除
                   //读完一个，读下一个，长度填满就下一个
                   stream >> _message_id >> _message_len;

                   //将buffer 中的前四个字节移除
                   _buffer = _buffer.mid(sizeof(quint16) * 2);

                   // 输出读取的数据
                   qDebug() << "Message ID:" << _message_id << ", Length:" << _message_len;

               }

                //buffer剩余长读是否满足消息体长度，不满足则退出继续等待接受
               if(_buffer.size() < _message_len){
                    _b_recv_pending = true;
                    return;
               }

               _b_recv_pending = false;
               // 读取消息体
               QByteArray messageBody = _buffer.mid(0, _message_len);
                qDebug() << "receive body msg is " << messageBody ;

               _buffer = _buffer.mid(_message_len);
           }

       });

       //5.15 之后版本
//       QObject::connect(&_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::errorOccurred), [&](QAbstractSocket::SocketError socketError) {
//           Q_UNUSED(socketError)
//           qDebug() << "Error:" << _socket.errorString();
//       });

       // 处理错误（适用于Qt 5.15之前的版本）有的时候socket连接可能会有异常
       // 这个时候，就可以绑定错误的信号，做一些处理
        QObject::connect(&_socket, static_cast<void (QTcpSocket::*)(QTcpSocket::SocketError)>(&QTcpSocket::error),
                            [&](QTcpSocket::SocketError socketError) {
               qDebug() << "Error:" << _socket.errorString() ;
               switch (socketError) {
                   case QTcpSocket::ConnectionRefusedError:
                       qDebug() << "Connection Refused!";
                       emit sig_con_success(false);
                       break;
                   case QTcpSocket::RemoteHostClosedError:
                       qDebug() << "Remote Host Closed Connection!";
                       break;
                   case QTcpSocket::HostNotFoundError:
                       qDebug() << "Host Not Found!";
                       emit sig_con_success(false);
                       break;
                   case QTcpSocket::SocketTimeoutError:
                       qDebug() << "Connection Timeout!";
                       emit sig_con_success(false);
                       break;
                   case QTcpSocket::NetworkError:
                       qDebug() << "Network Error!";
                       break;
                   default:
                       qDebug() << "Other Error!";
                       break;
               }
         });

        // 处理连接断开
        QObject::connect(&_socket, &QTcpSocket::disconnected, [&]() {
            qDebug() << "Disconnected from server.";
        });

        QObject::connect(this, &TcpMgr::sig_send_data, this, &TcpMgr::slot_send_data);

        initHandlers();
}

TcpMgr::~TcpMgr(){

}

void TcpMgr::initHandlers()
{
    //这里不用这个方法是因为，shared_from_this 需要构造函数执行完
    //由于initHandlers在构造函数中调用，所以这里就不再需要
    //auto self = shared_from_this();
    _handlers.insert(ID_CHAT_LOGIN_RSP, [this](ReqId id, int len, QByteArray data){
        Q_UNUSED(len);
        qDebug()<< "handle id is "<< id << " data is " << data;
        // 将QByteArray转换为QJsonDocument
        QJsonDocument jsonDoc = QJsonDocument::fromJson(data);

        // 检查转换是否成功
        if(jsonDoc.isNull()){
           qDebug() << "Failed to create QJsonDocument.";
           return;
        }

        QJsonObject jsonObj = jsonDoc.object();

        if(!jsonObj.contains("error")){
            int err = ErrorCodes::ERR_JSON;
            qDebug() << "Login Failed, err is Json Parse Err" << err ;
            emit sig_login_failed(err);
            return;
        }

        int err = jsonObj["error"].toInt();
        if(err != ErrorCodes::SUCCESS){
            qDebug() << "Login Failed, err is " << err ;
            emit sig_login_failed(err);
            return;
        }

        UserMgr::GetInstance()->SetUid(jsonObj["uid"].toInt());
        UserMgr::GetInstance()->SetName(jsonObj["name"].toString());
        UserMgr::GetInstance()->SetToken(jsonObj["token"].toString());
        emit sig_swich_chatdlg();
    });
}

void TcpMgr::handleMsg(ReqId id, int len, QByteArray data)
{
   auto find_iter =  _handlers.find(id);
   if(find_iter == _handlers.end()){
        qDebug()<< "not found id ["<< id << "] to handle";
        return ;
   }

   find_iter.value()(id,len,data);
}


void TcpMgr::slot_tcp_connect(ServerInfo si)
{
    qDebug()<< "receive tcp connect signal";
    // 尝试连接到服务器
    qDebug() << "Connecting to server...";
    _host = si.Host;
    _port = static_cast<uint16_t>(si.Port.toUInt());
    _socket.connectToHost(si.Host, _port);
}

void TcpMgr::slot_send_data(ReqId reqId, QString data)
{
    uint16_t id = reqId;

    // 将字符串转换为UTF-8编码的字节数组
    QByteArray dataBytes = data.toUtf8();

    // 计算长度（使用网络字节序转换）
    quint16 len = static_cast<quint16>(data.size());

    // 创建一个QByteArray用于存储要发送的所有数据
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);

    // 设置数据流使用网络字节序
    /*
在网络编程中，设置大端字节序（Big Endian）是为了确保不同平台之间的数据传输一致性和兼容性。以下是更详细的原因：
1. 统一的数据格式

平台差异：不同的计算机架构可能采用不同的字节序，例如：
大端（Big Endian）：将最重要的字节放在最低的地址上。
小端（Little Endian）：将最重要的字节放在最高的地址上。


标准化：为了避免在数据传输中因字节序不同而导致的数据解释错误，网络协议（如 TCP/IP）规定使用大端字节序。这意味着所有网络传输的数据在字节序上都是统一的。

2. 兼容性

跨平台通信：在互联网中，数据可能在不同操作系统和硬件架构之间传输。如果发送方和接收方使用不同的字节序，会导致接收方读取数据时出现问题，进而导致逻辑错误或数据损坏。

避免复杂性：通过统一使用大端字节序，程序员不用关心接收方和发送方的字节序匹配，降低了编程的复杂性。


3. 历史原因

行业标准：许多网络协议（如 TCP/IP 协议族）和文件格式都采用大端字节序。这已经成为一种行业标准，遵循这种约定可以确保与其他设备或系统的良好兼容。

4. 示例
举个简单的例子，如果一个整数 0x12345678（在小端中表示为 78 56 34 12），在大端中，它将表示为 12 34 56 78。如果在小端系统上发送这个数据而接收方是大端系统，接收方会错误地理解这个值。
小结

为了确保：通过使用大端字节序，可以确保在网络通信中各类设备之间的兼容性和一致性。
减少错误：避免了因字节序不一致导致的解释错误，使得数据的传输和解析更加可靠。

因此，在设计网络协议和网络应用程序时，设置大端字节序是一个重要的考虑因素。
*/
    out.setByteOrder(QDataStream::BigEndian);

    // 写入ID和长度
    out << id << len;

    // 添加字符串数据
    block.append(data);

    // 发送数据
    _socket.write(block);
}



